#include "httptimpl.h"
#include <utilex/getpath.hpp>
#include <compat/sockinet.h>
#include <compat/str.h>

CHttptImpl::CHttptImpl()
{
	m_httpcontent_length	= 0;
	m_Notify				= NULL;
	m_hstid					= HTTP_IMPL_START;

}
CHttptImpl::~CHttptImpl(void)
{

}
void CHttptImpl::BindHttp(IBase* pIAsyncTcpSocket, IBase* pIOper, IBase* pIDns)
{
	m_pIAsyncTcpSocket		=	pIAsyncTcpSocket;
	m_pIOper				=	pIOper;
	m_pIDns					=	pIDns;
	http_init_analyze(http_response);


	ULONG BufLen = 16 * 1024;
	m_pIOper->SetIoLen(BufLen);
	sockInit(m_pIAsyncTcpSocket, BufLen, BufLen, 20, 20, 5);
}
//////////////////////////////////////////////////////////////////////////

std_method_impl CHttptImpl::SetCookies(http_header_data *data)
{
	return S_OK;
}

std_method_impl CHttptImpl::SetNotify(IHttpNotify *Notify)
{
	retrtpv(Notify, E_FAIL)
	m_Notify = Notify;
	return S_OK;
}
std_method_impl CHttptImpl::Perform(ip_addr_s url, http_addr_s scheme,ip_port uport)
{
	OnBuildHttp(url, scheme,uport);
	return OnConnect(url, uport);
}
std_method_type_impl(http_header) CHttptImpl::GetHttpHead()
{
	return m_szhttpHead;
}
std_method_impl CHttptImpl::ReSet()
{
	Cancel();
	return S_OK;
}
std_method_impl CHttptImpl::Cancel()
{
	httpEventComplate(m_pIAsyncTcpSocket, m_pIOper);
	m_hstid = HTTP_IMPL_START;
	http_init_analyze(http_response);

	return S_OK;
}
std_method_type_impl(int) CHttptImpl::IsComplete()
{
	return m_parser->content_length == 0;
}
std_method_impl CHttptImpl::OnOperationComplete(IIoOperation *pOperation)
{
	ERROR_CODE	errcode;
	ULONG		TransferedBytes = 0;
	HRESULT		hr = S_OK;

	pOperation->GetResult(&errcode, &TransferedBytes);

	if (errcode){

		char szlog[128] = { 0x00 };
		sprintf(szlog, "AT %s: CHttpImpl OnOperationComplete error : %lu \n", AT, errcode);
		iosprintf(szlog);

		if (OP_EOF == errcode) {
			m_hstid = HTTP_IMPL_END;
		} else {
			m_hstid = Id_Faild(m_pIOper->GetId());
		}

		Notify(m_hstid);

	} else {
		hr = httpEventProc(m_pIOper->GetId(), pOperation);

		m_hstid = Notify(m_pIOper->GetId());

		hr = httpEvent(m_hstid, m_pIAsyncTcpSocket, pOperation);

		return hr;
	}
	return S_OK;
}

std_method_impl CHttptImpl::DnsResultIP(int error, const char* url, const char* ip)
{	
	if (m_hstid < HTTP_IMPL_CONNECTING && DNS_PROC_RES_OK == error) {

		iosprintf(ip);
		m_pIOper->SetAddress(ip);
		m_pIOper->SetConPort(m_uport);
		httpConnect(m_pIOper.m_p, ip, m_uport);
		m_hstid = HTTP_IMPL_CONNECTING;
		return E_FAIL;
	}
	else {
		if (DNS_PROC_RES_COMPLETE == error) {
			iosprintf("http->DNS_PROC_RES_COMPLETE")
		}
		else if (DNS_PROC_RES_ERROR == error)
		{
			iosprintf("http->DNS_PROC_RES_ERROR")
		}
		else {
			iosprintf("http->DNS_ERROR")
		}
		return E_FAIL;
	}
	return S_OK;
}
ULONG CHttptImpl::Notify(ULONG ulevent)
{
	retrtp(m_Notify, HTTP_IMPL_UNKNOWN)
	return m_Notify->Event(ulevent);
}
ULONG CHttptImpl::Id_Suc(ULONG ulid) {

	ulid++;
	ulid++;
	return ulid;
}
std_method_impl CHttptImpl::httpEventProc(ULONG ulevent, IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	switch (ulevent)
	{
	case HTTP_IMPL_CONNECTING:
		httpConnected(pOperation);
		break;
	case HTTP_IMPL_SEND_HEAD:
		httpSendHeaded(pOperation);
		break;
	case HTTP_IMPL_SEND_BODY:
		httpSendBodyed(pOperation);
		break;
	case HTTP_IMPL_RECV_HEAD:
		httpRecvHeaded(pOperation);
		break;
	case HTTP_IMPL_RECV_BODY:
		httpRecvBodyed(pOperation);
		break;
	default:
		break;
	}

	return hr;
}
std_method_impl CHttptImpl::httpConnected(IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pOperation->SetId(HTTP_IMPL_CONNECTED);

	return hr;
}
std_method_impl CHttptImpl::httpSendHeaded(IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pOperation->SetId(HTTP_IMPL_SEND_HEADED);

	return hr;
}
std_method_impl CHttptImpl::httpSendBodyed(IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pOperation->SetId(HTTP_IMPL_SEND_BODYED);

	return hr;
}
std_method_impl CHttptImpl::httpRecvHeaded(IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pOperation->SetId(HTTP_IMPL_RECV_HEADED);

	ERROR_CODE errcode = 0;
	ULONG pTransferedBytes = 0;

	pOperation->GetResult(&errcode, &pTransferedBytes);

	ULONG pIoStartPos = 0;
	ULONG pIoExpectBytes = 0;
	pOperation->GetIoParam(&pIoStartPos, &pIoExpectBytes);
	
	char* p = (char*)pOperation->GetIo(pIoStartPos);
	if (!p) {
		return E_FAIL;
	}

	m_parser->p = p;
	http_analyze(p, pTransferedBytes);
	
	return hr;
}
std_method_impl CHttptImpl::httpRecvBodyed(IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pOperation->SetId(HTTP_IMPL_RECV_BODYED);
	ERROR_CODE errcode = 0;
	ULONG pTransferedBytes = 0;
	pOperation->GetResult(&errcode, &pTransferedBytes);

	ULONG pIoStartPos = 0;
	ULONG pIoExpectBytes = 0;
	pOperation->GetIoParam(&pIoStartPos, &pIoExpectBytes);


	char* p = (char*)pOperation->GetIo(pIoStartPos);
	if (!p) {
		return E_FAIL;
	}

	m_parser->p = p;
	http_analyze(p, pTransferedBytes);

	return hr;
}

std_method_impl CHttptImpl::httpEvent(ULONG ulevent, IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	HRESULT hr = E_FAIL;

	switch (ulevent)
	{
	case HTTP_IMPL_SEND_HEAD:
		hr = httpSendHead(pIoDevice, pOperation);
		break;
	case HTTP_IMPL_SEND_BODY:
		hr = httpSendBody(pIoDevice, pOperation);
		break;
	case HTTP_IMPL_RECV_HEAD:
		hr = httpRecvHead(pIoDevice, pOperation);
		break;
	case HTTP_IMPL_RECV_BODY:
		hr = httpRecvBody(pIoDevice, pOperation);
		break;
	case HTTP_IMPL_END:
		hr = S_OK;
		break;
	default:
		break;
	}
	return hr;
}
std_method_impl CHttptImpl::httpEventComplate(IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	HRESULT hr = S_OK;

	pIoDevice->CloseIo();
	pOperation->Cancel();

	return hr;
}
ULONG CHttptImpl::Id_Faild(ULONG ulid) {

	ulid++;
	return ulid;
}
ULONG CHttptImpl::sockInit(IAsyncTcpSocket* pIAsyncTcpSocket, ULONG sbuflen, ULONG rbuflen,
				int iwtimout, int irtimout, int iconntimout)
{
	pIAsyncTcpSocket->OpenIo();
	pIAsyncTcpSocket->BindIo();
	pIAsyncTcpSocket->SendBufLen(sbuflen);
	pIAsyncTcpSocket->RecvBufLen(rbuflen);
	pIAsyncTcpSocket->SendTimeOut(iwtimout);
	pIAsyncTcpSocket->RecvTimeOut(irtimout);
	pIAsyncTcpSocket->ConnectTimeOut(iconntimout);

	return 1;
}

std_method_impl CHttptImpl::httpConnect(IIoOperation *pOperation, ip_addr_s addr, ip_port uport)
{

	iosprintf("CHttpClientImpl::httpConnect")
	iosprintf(addr)

	os_io_fd* fd = pOperation->GetBind();
	fd->type = addr_socket_v4;
	fd->fd.socket = m_pIAsyncTcpSocket->GetSocket();
	pOperation->SetAddress(addr);
	pOperation->SetConPort(uport);
	pOperation->SetAsynRW(false);
	pOperation->SetMultiThread(true);
	pOperation->SetId(HTTP_IMPL_CONNECTING);
	return m_pIAsyncTcpSocket->ConnectIo(pOperation);
}
std_method_impl CHttptImpl::httpSendHead(IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	const char* p = m_http_resheader.build_res();
	size_t ulen = m_http_resheader.build_res_len();

	ULONG uSize = pOperation->GetIoLen();
	ULONG Sendlen = m_Notify->SendHead((void*)p, uSize, ulen);
	pOperation->AttachIoBuf((BUFFER_PTR)p, 0, Sendlen);

	return httpSend(HTTP_IMPL_SEND_HEAD, pIoDevice, pOperation);
}
std_method_impl CHttptImpl::httpSendBody(IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	ULONG uSize = pOperation->GetIoLen();
	ULONG Sendlen = m_Notify->SendBody((void*)pOperation->GetIo(0), uSize, uSize);
	
	return httpSend(HTTP_IMPL_SEND_BODY, pIoDevice, pOperation);
}
std_method_impl CHttptImpl::httpRecvHead(IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	ULONG uSize = pOperation->GetIoLen();
	pOperation->SetIoParam(0, 0);
	return httpRecv(HTTP_IMPL_RECV_HEAD, pIoDevice, pOperation);
}
std_method_impl CHttptImpl::httpRecvBody(IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	ULONG uSize = pOperation->GetIoLen();
	if (uSize < m_parser->content_length) {
		pOperation->SetIoParam(0, uSize);
	}
	else {
		uSize = m_parser->content_length & UINT64_MAX;
		pOperation->SetIoParam(0, uSize);
	}
	return httpRecv(HTTP_IMPL_RECV_BODY, pIoDevice, pOperation);
}

std_method_impl CHttptImpl::httpSend(ULONG uid, IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	pOperation->SetId(uid);
	return pIoDevice->WriteIo(pOperation);
}
std_method_impl CHttptImpl::httpRecv(ULONG uid, IIoDevice* pIoDevice, IIoOperation *pOperation)
{
	pOperation->SetId(uid);
	return pIoDevice->ReadIo(pOperation);
}
void CHttptImpl::on_http_message_begin()
{
	retret(m_parser)
}
void CHttptImpl::on_http_header_complete()
{
	retret(m_parser)
	m_httpcontent_length = m_parser->content_length;
	m_szhttpHead = new char[m_parser->nread + 1]();
	memcpy(m_szhttpHead, m_parser->p, m_parser->nread);
}
void CHttptImpl::on_http_body(const char* at, size_t length)
{		
	retret(m_parser)
	m_Notify->RecvBody((void*)at, length, 1);
}
void CHttptImpl::on_http_message_complete()
{
	retret(m_parser)
}

std_method_impl CHttptImpl::OnConnect(ip_addr_s url, ip_port uport)
{
	HRESULT		hr = S_OK;

	m_uport = uport;

	if (is_ip(url)) {
		hr = httpConnect(m_pIOper.m_p, url, m_uport);
	}
	else {
		hr = m_pIDns->GetURLtoHost(url, "", "http", this, NULL, NULL, 1);
	}
	return hr;
}
std_method_impl CHttptImpl::OnBuildHttp(ip_addr_s url, http_addr_s scheme, ip_port uport)
{
	m_http_resheader.set_method(scheme);
	
	char szPort[32] = { 0x00 };
	snprintf(szPort, sizeof(szPort), ":%lu", uport);
	string strurl(url);
	strurl.append(szPort);
	m_http_resheader.set_host(strurl.c_str());
	m_http_resheader.set_accept("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
	m_http_resheader.set_accept_language("zh-CN");
	m_http_resheader.set_cache_control("no-cache");
	m_http_resheader.set_accept_encoding("gzip, deflate, br");
	m_http_resheader.set_connection("close");
	m_http_resheader.set_use_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 13.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134");
	m_http_resheader.build_res_http();


	return S_OK;
}




